package model.audio;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;

import model.audio.Interface.IAudioTransmissionModel;
import model.audio.Interface.IMixer;
import model.audio.Interface.IPObserver;
import model.audio.Interface.ITransmission;


public class AudioTransmissionModel implements Runnable,ITransmission,IAudioTransmissionModel,IPObserver{

	private DatagramSocket socket=null;
	private DatagramPacket sendP=null;
	private DatagramPacket receiveP=null;
	private Thread thread=null;
	private ArrayList<InetSocketAddress> clientAddresses=null;
	private byte receiveBuffer[];
	private IMixer mixer=null;
	private boolean isStart;
	
	public static final int PORT=5000;
	//以下为连接服务器进行NAT穿透用
	public static final int AUDIO_SERVER_PORT=5000;
	public static final int DEFAULT_TRIES=3;
	public static final int DEFAULT_EXPIRE_MILLIS=2000;
	public static final int MAX_UDP_BUFFER_SIZE=508;
	public static final String SERVER_IP="127.0.0.1";
	
	public AudioTransmissionModel(IMixer mixer,ArrayList<InetSocketAddress> targetAddresses,int receiveBufferLength){
		this.mixer=mixer;
		isStart=true;
		this.clientAddresses=targetAddresses;
		try {
			socket=new DatagramSocket(PORT);
		} catch (SocketException e) {
			e.printStackTrace();
		}
		sendP=new DatagramPacket(new byte[0], 0);
		receiveBuffer=new byte[receiveBufferLength];
		receiveP=new DatagramPacket(receiveBuffer, receiveBuffer.length);
	}
	
	@Override
	public void sendAudioData(byte[] data) {
		// TODO Auto-generated method stub
		if(clientAddresses!=null){
				sendP.setData(data);
				sendP.setLength(data.length);
			for(InetSocketAddress ad:clientAddresses){
				sendP.setSocketAddress(ad);
				try {	
				socket.send (sendP);
				} catch (IOException e) {
					//e.printStackTrace();
				}
			}
		}
	}

	@Override
	public void updateAddresses(ArrayList<InetSocketAddress> ads) {
		// TODO Auto-generated method stub
		clientAddresses=new ArrayList<InetSocketAddress>();
		for(InetSocketAddress ad:ads){
			//System.out.println("*&^"+ad.getAddress().getHostAddress());
			InetSocketAddress address=new InetSocketAddress(ad.getAddress(),ad.getPort());
			clientAddresses.add(address);
		}
		//System.out.println("-------------");
		mixer.updateSourceClientList(clientAddresses);
	}
	
	@Override
	public void startAudioTransmission() {
		// TODO Auto-generated method stub
		if(thread == null || !thread.isAlive()){
			try {
				socket.setSoTimeout(0);
			} catch (SocketException e) {
				e.printStackTrace();
			}
			thread = new Thread(this);
			isStart = true;
			thread.start();
		}
	}

	@Override
	public void stopAudioTransmission() {
		// TODO Auto-generated method stub
		thread.interrupt();
		isStart=false;
		socket.close();
	}
	
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while(isStart){
			try {
				socket.receive(receiveP);
				receiveBuffer=receiveP.getData();				
				mixer.mix((InetSocketAddress) receiveP.getSocketAddress(),receiveBuffer);
			} catch (IOException e) {
				//e.printStackTrace();
			} 
		}
	}
	
	public boolean connect(int userID){
		try {
			socket.setSoTimeout(DEFAULT_EXPIRE_MILLIS);
		} catch (SocketException e1) {
			e1.printStackTrace();
		}
		ByteArrayOutputStream byteOut=new ByteArrayOutputStream();
		DataOutputStream dataOut=new DataOutputStream(byteOut);
		try{
			dataOut.writeInt(userID);
			dataOut.close();
		}catch(IOException e){
			e.printStackTrace();
		}
		DatagramPacket outPacket=null;
		try {
			outPacket=new DatagramPacket(byteOut.toByteArray(),byteOut.size(),InetAddress.getByName(SERVER_IP),AUDIO_SERVER_PORT);
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		DatagramPacket inPacket=new DatagramPacket(new byte[MAX_UDP_BUFFER_SIZE],MAX_UDP_BUFFER_SIZE);
		for(int i=0;i<DEFAULT_TRIES;i++){
			try {
				socket.send(outPacket);
				socket.receive(inPacket);
			} catch (IOException e) {
				e.printStackTrace();
			}	
			ByteArrayInputStream byteIn=new ByteArrayInputStream(inPacket.getData(),0,inPacket.getLength());
			DataInputStream dataIn=new DataInputStream(byteIn);			
			try{
				int inValue=dataIn.readInt();
				if(userID==inValue){
					return true;
				}
			}catch(IOException e){
				e.printStackTrace();
			}			
			inPacket.setLength(MAX_UDP_BUFFER_SIZE);
		}
		return false;
	}

}
